// -*- mode:c++ -*-

// Copyright (c) 2010, 2012, 2014, 2016 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Copyright (c) 2007-2008 The Florida State University
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


def template PanicExecute {{
    Fault
    %(class_name)s::execute(ExecContext *xc,
                            trace::InstRecord *traceData) const
    {
        panic("Execute function executed when it shouldn't be!\n");
        return NoFault;
    }
}};

def template PanicInitiateAcc {{
    Fault
    %(class_name)s::initiateAcc(ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        panic("InitiateAcc function executed when it shouldn't be!\n");
        return NoFault;
    }
}};

def template PanicCompleteAcc {{
    Fault
    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        panic("CompleteAcc function executed when it shouldn't be!\n");
        return NoFault;
    }
}};


def template SwapExecute {{
    Fault
    %(class_name)s::execute(ExecContext *xc,
                            trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        uint64_t memData = 0;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            %(preacc_code)s;

            if (fault == NoFault) {
                fault = writeMemAtomicLE(xc, traceData, Mem, EA,
                        memAccessFlags, &memData);
            }

            if (fault == NoFault) {
                %(postacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template SwapInitiateAcc {{
    Fault
    %(class_name)s::initiateAcc(ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        uint64_t memData = 0;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            %(preacc_code)s;

            if (fault == NoFault) {
                fault = writeMemTimingLE(xc, traceData, Mem, EA,
                        memAccessFlags, &memData);
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template SwapCompleteAcc {{
    Fault
    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;

        if (%(predicate_test)s) {
            // ARM instructions will not have a pkt if the predicate is false
            getMemLE(pkt, Mem, traceData);
            uint64_t memData = Mem;

            %(postacc_code)s;

            if (fault == NoFault) {
                %(op_wb)s;
            }
        }

        return fault;
    }
}};

def template LoadExecute {{
    Fault
    %(class_name)s::execute(ExecContext *xc,
                            trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                fault = readMemAtomicLE(
                        xc, traceData, EA, Mem, memAccessFlags);
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template NeonLoadExecute {{
    template <class Element>
    Fault
    %(class_name)s<Element>::execute(
            ExecContext *xc, trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(mem_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        MemUnion memUnion;
        uint8_t *dataPtr = memUnion.bytes;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                const auto size = %(size)d;
                fault = readMemAtomic(xc, EA, dataPtr,
                                      size, memAccessFlags,
                                      std::vector<bool>(size, true));
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template StoreExecute {{
    Fault
    %(class_name)s::execute(ExecContext *xc,
                            trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                fault = writeMemAtomicLE(xc, traceData, Mem, EA,
                        memAccessFlags, NULL);
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template NeonStoreExecute {{
    template <class Element>
    Fault
    %(class_name)s<Element>::execute(
            ExecContext *xc, trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(mem_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        MemUnion memUnion;
        uint8_t *dataPtr = memUnion.bytes;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                const auto size = %(size)d;
                fault = writeMemAtomic(xc, dataPtr, EA, size,
                                       memAccessFlags, NULL,
                                       std::vector<bool>(size, true));
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template StoreExExecute {{
    Fault
    %(class_name)s::execute(ExecContext *xc,
                            trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            uint64_t writeResult;

            if (fault == NoFault) {
                fault = writeMemAtomicLE(xc, traceData, Mem, EA,
                        memAccessFlags, &writeResult);
            }

            if (fault == NoFault) {
                %(postacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template StoreExInitiateAcc {{
    Fault
    %(class_name)s::initiateAcc(ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                fault = writeMemTimingLE(xc, traceData, Mem, EA,
                        memAccessFlags, NULL);
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template StoreInitiateAcc {{
    Fault
    %(class_name)s::initiateAcc(ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                fault = writeMemTimingLE(xc, traceData, Mem, EA,
                        memAccessFlags, NULL);
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template NeonStoreInitiateAcc {{
    template <class Element>
    Fault
    %(class_name)s<Element>::initiateAcc(
            ExecContext *xc, trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(mem_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            MemUnion memUnion;
            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                const auto size = %(size)d;
                fault = writeMemTiming(xc, memUnion.bytes, EA,
                                       size, memAccessFlags, nullptr,
                                       std::vector<bool>(size, true));
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template LoadInitiateAcc {{
    Fault
    %(class_name)s::initiateAcc(ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_src_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                fault = initiateMemRead(xc, traceData, EA, Mem,
                                        memAccessFlags);
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template NeonLoadInitiateAcc {{
    template <class Element>
    Fault
    %(class_name)s<Element>::initiateAcc(
            ExecContext *xc, trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(mem_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (%(predicate_test)s) {
            if (fault == NoFault) {
                const auto size = %(size)d;
                fault = initiateMemRead(xc, EA, size, memAccessFlags,
                                        std::vector<bool>(size, true));
            }
        } else {
            xc->setPredicate(false);
        }

        return fault;
    }
}};

def template LoadCompleteAcc {{
    Fault
    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;

        if (%(predicate_test)s) {
            // ARM instructions will not have a pkt if the predicate is false
            getMemLE(pkt, Mem, traceData);

            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        }

        return fault;
    }
}};

def template NeonLoadCompleteAcc {{
    template <class Element>
    Fault
    %(class_name)s<Element>::completeAcc(
            PacketPtr pkt, ExecContext *xc, trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(mem_decl)s;
        %(op_decl)s;
        %(op_rd)s;

        if (%(predicate_test)s) {
            // ARM instructions will not have a pkt if the predicate is false
            MemUnion &memUnion = *(MemUnion *)pkt->getPtr<uint8_t>();

            if (fault == NoFault) {
                %(memacc_code)s;
            }

            if (fault == NoFault) {
                %(op_wb)s;
            }
        }

        return fault;
    }
}};

def template StoreCompleteAcc {{
    Fault
    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        return NoFault;
    }
}};

def template NeonStoreCompleteAcc {{
    template <class Element>
    Fault
    %(class_name)s<Element>::completeAcc(
            PacketPtr pkt, ExecContext *xc, trace::InstRecord *traceData) const
    {
        return NoFault;
    }
}};

def template StoreExCompleteAcc {{
    Fault
    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
                                trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;

        if (%(predicate_test)s) {
            uint64_t writeResult = pkt->req->getExtraData();
            %(postacc_code)s;

            if (fault == NoFault) {
                %(op_wb)s;
            }
        }

        return fault;
    }
}};

def template RfeDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _base, int _mode, bool _wb);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template SrsDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _regMode, int _mode, bool _wb);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template SwapDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _op1, uint32_t _base);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template LoadStoreDImmDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _dest2,
                uint32_t _base, bool _add, int32_t _imm);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template StoreExDImmDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _result, uint32_t _dest, uint32_t _dest2,
                uint32_t _base, bool _add, int32_t _imm);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template LoadStoreImmDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _base, bool _add, int32_t _imm);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;

        void
        annotateFault(ArmISA::ArmFault *fault) override
        {
            %(fa_code)s
        }
    };
}};

def template StoreExImmDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _result, uint32_t _dest, uint32_t _base,
                bool _add, int32_t _imm);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template StoreDRegDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _dest2,
                uint32_t _base, bool _add,
                int32_t _shiftAmt, uint32_t _shiftType,
                uint32_t _index);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template StoreRegDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _base, bool _add,
                int32_t _shiftAmt, uint32_t _shiftType,
                uint32_t _index);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;

        void
        annotateFault(ArmISA::ArmFault *fault) override
        {
            %(fa_code)s
        }
    };
}};

def template LoadDRegDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _dest2,
                uint32_t _base, bool _add,
                int32_t _shiftAmt, uint32_t _shiftType,
                uint32_t _index);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;
    };
}};

def template LoadRegDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _base, bool _add,
                int32_t _shiftAmt, uint32_t _shiftType,
                uint32_t _index);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;

        void
        annotateFault(ArmISA::ArmFault *fault) override
        {
            %(fa_code)s
        }
    };
}};

def template LoadImmDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      private:
        %(reg_idx_arr_decl)s;

      public:
        /// Constructor.
        %(class_name)s(ExtMachInst machInst,
                uint32_t _dest, uint32_t _base, bool _add, int32_t _imm);

        Fault execute(ExecContext *, trace::InstRecord *) const override;
        Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
        Fault completeAcc(PacketPtr, ExecContext *,
                          trace::InstRecord *) const override;

        void
        annotateFault(ArmISA::ArmFault *fault) override
        {
            %(fa_code)s
        }
    };
}};

def template RfeConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _base, int _mode, bool _wb) :
        %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                       (RegIndex)_base, (AddrMode)_mode, _wb)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        uops = new StaticInstPtr[1 + %(use_wb)d + %(use_pc)d];
        int uopIdx = 0;
        uops[uopIdx] = new %(acc_name)s(machInst, _base, _mode, _wb);
        uops[uopIdx]->setDelayedCommit();
#if %(use_wb)d
        uops[++uopIdx] = new %(wb_decl)s;
        uops[uopIdx]->setDelayedCommit();
#endif
#if %(use_pc)d
        uops[++uopIdx] = new %(pc_decl)s;
#endif
        uops[0]->setFirstMicroop();
        uops[uopIdx]->setLastMicroop();
#endif
    }
}};

def template SrsConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _regMode, int _mode, bool _wb) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (OperatingMode)_regMode, (AddrMode)_mode, _wb)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _regMode, _mode, _wb);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template SwapConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _op1, uint32_t _base) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_op1, (RegIndex)_base)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
    }
}};

def template LoadStoreDImmConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _dest2,
            uint32_t _base, bool _add, int32_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_dest2,
                 (RegIndex)_base, _add, _imm)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add, _imm);
        uops[0]->setFirstMicroop();
        uops[0]->setDelayedCommit();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template StoreExDImmConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _result, uint32_t _dest, uint32_t _dest2,
            uint32_t _base, bool _add, int32_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_result, (RegIndex)_dest, (RegIndex)_dest2,
                 (RegIndex)_base, _add, _imm)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _result, _dest, _dest2,
                                   _base, _add, _imm);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template LoadStoreImmConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _base, bool _add, int32_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_base, _add, _imm)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _dest, _base, _add, _imm);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template StoreExImmConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _result, uint32_t _dest, uint32_t _base,
            bool _add, int32_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_result, (RegIndex)_dest,
                 (RegIndex)_base, _add, _imm)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _result, _dest,
                                   _base, _add, _imm);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template StoreDRegConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _dest2, uint32_t _base, bool _add,
            int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_dest2,
                 (RegIndex)_base, _add,
                 _shiftAmt, (ArmShiftType)_shiftType,
                 (RegIndex)_index)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add,
                                   _shiftAmt, _shiftType, _index);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template StoreRegConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _base, bool _add,
            int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_base, _add,
                 _shiftAmt, (ArmShiftType)_shiftType,
                 (RegIndex)_index)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        uops[0] = new %(acc_name)s(machInst, _dest, _base, _add,
                                   _shiftAmt, _shiftType, _index);
        uops[0]->setDelayedCommit();
        uops[0]->setFirstMicroop();
        uops[1] = new %(wb_decl)s;
        uops[1]->setLastMicroop();
#endif
    }
}};

def template LoadDRegConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _dest2, uint32_t _base, bool _add,
            int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_dest2,
                 (RegIndex)_base, _add,
                 _shiftAmt, (ArmShiftType)_shiftType,
                 (RegIndex)_index)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        if ((_dest == _index) || (_dest2 == _index)) {
            RegIndex wbIndexReg = int_reg::Ureg0;
            uops[0] = new MicroUopRegMov(machInst, int_reg::Ureg0, _index);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add,
                                       _shiftAmt, _shiftType, _index);
            uops[1]->setDelayedCommit();
            uops[2] = new %(wb_decl)s;
            uops[2]->setLastMicroop();
        } else {
            RegIndex wbIndexReg = index;
            uops[0] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add,
                                       _shiftAmt, _shiftType, _index);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(wb_decl)s;
            uops[1]->setLastMicroop();
        }
#endif
    }
}};

def template LoadRegConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _base, bool _add,
            int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_base, _add,
                 _shiftAmt, (ArmShiftType)_shiftType,
                 (RegIndex)_index)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        [[maybe_unused]] bool conditional = false;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            conditional = true;
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
            RegIndex wbIndexReg = index;
            uops[0] = new %(acc_name)s(machInst, int_reg::Ureg0, _base, _add,
                                       _shiftAmt, _shiftType, _index);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(wb_decl)s;
            uops[1]->setDelayedCommit();
            uops[2] = new MicroUopRegMov(machInst, int_reg::Pc,
                                         int_reg::Ureg0);
            uops[2]->setFlag(StaticInst::IsControl);
            uops[2]->setFlag(StaticInst::IsIndirectControl);
            if (conditional)
                uops[2]->setFlag(StaticInst::IsCondControl);
            else
                uops[2]->setFlag(StaticInst::IsUncondControl);
            uops[2]->setLastMicroop();
        } else if(_dest == _index) {
            RegIndex wbIndexReg = int_reg::Ureg0;
            uops[0] = new MicroUopRegMov(machInst, int_reg::Ureg0, _index);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(acc_name)s(machInst, _dest, _base, _add,
                                      _shiftAmt, _shiftType, _index);
            uops[1]->setDelayedCommit();
            uops[2] = new %(wb_decl)s;
            uops[2]->setLastMicroop();
        } else {
            RegIndex wbIndexReg = index;
            uops[0] = new %(acc_name)s(machInst, _dest, _base, _add,
                                      _shiftAmt, _shiftType, _index);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(wb_decl)s;
            uops[1]->setLastMicroop();

        }
#else
        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
            flags[IsControl] = true;
            flags[IsIndirectControl] = true;
            if (conditional)
                flags[IsCondControl] = true;
            else
                flags[IsUncondControl] = true;
        }
#endif
    }
}};

def template LoadImmConstructor {{
    %(class_name)s::%(class_name)s(ExtMachInst machInst,
            uint32_t _dest, uint32_t _base, bool _add, int32_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 (RegIndex)_dest, (RegIndex)_base, _add, _imm)
    {
        %(set_reg_idx_arr)s;
        %(constructor)s;
        [[maybe_unused]] bool conditional = false;
        if (!(condCode == COND_AL || condCode == COND_UC)) {
            conditional = true;
            for (int x = 0; x < _numDestRegs; x++) {
                setSrcRegIdx(_numSrcRegs++, destRegIdx(x));
            }
        }
#if %(use_uops)d
        assert(numMicroops >= 2);
        uops = new StaticInstPtr[numMicroops];
        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
            uops[0] = new %(acc_name)s(machInst, int_reg::Ureg0, _base, _add,
                                   _imm);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(wb_decl)s;
            uops[1]->setDelayedCommit();
            uops[2] = new MicroUopRegMov(
                    machInst, int_reg::Pc, int_reg::Ureg0);
            uops[2]->setFlag(StaticInst::IsControl);
            uops[2]->setFlag(StaticInst::IsIndirectControl);
            /* Also set flags on the macroop so that pre-microop decomposition
                branch prediction can work */
            setFlag(StaticInst::IsControl);
            setFlag(StaticInst::IsIndirectControl);
            if (conditional) {
                uops[2]->setFlag(StaticInst::IsCondControl);
                setFlag(StaticInst::IsCondControl);
            } else {
                uops[2]->setFlag(StaticInst::IsUncondControl);
                setFlag(StaticInst::IsUncondControl);
            }
            if (_base == int_reg::Sp && _add && _imm == 4 && %(is_ras_pop)s) {
                uops[2]->setFlag(StaticInst::IsReturn);
                setFlag(StaticInst::IsReturn);
            }
            uops[2]->setLastMicroop();
        } else {
            uops[0] = new %(acc_name)s(machInst, _dest, _base, _add, _imm);
            uops[0]->setDelayedCommit();
            uops[0]->setFirstMicroop();
            uops[1] = new %(wb_decl)s;
            uops[1]->setLastMicroop();
        }
#else
        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
            flags[IsControl] = true;
            flags[IsIndirectControl] = true;
            if (conditional)
                flags[IsCondControl] = true;
            else
                flags[IsUncondControl] = true;
        }
#endif
    }
}};
